package com.nervenets.web.controller.wealth;

import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
import com.github.binarywang.wxpay.bean.entpay.EntPayResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.nervenets.Global;
import com.nervenets.utils.HttpTools;
import com.nervenets.utils.NumberUtil;
import com.nervenets.web.controller.BaseController;
import com.nervenets.web.controller.TokenParams;
import com.nervenets.web.controller.user.UserWealthChangeParams;
import com.nervenets.web.controller.user.UserWealthPagingParams;
import com.nervenets.web.controller.user.WithdrawCheckParams;
import com.nervenets.web.controller.user.WithdrawParams;
import com.nervenets.web.entity.KeyValue;
import com.nervenets.web.entity.MessageCode;
import com.nervenets.web.entity.ResponseResult;
import com.nervenets.web.enumeration.*;
import com.nervenets.web.hibernate.dao.UserDao;
import com.nervenets.web.hibernate.dao.WealthFlowDao;
import com.nervenets.web.hibernate.domain.user.User;
import com.nervenets.web.hibernate.domain.WealthFlow;
import com.nervenets.web.rabbitmq.FlowMessage;
import com.nervenets.web.rabbitmq.FlowType;
import com.nervenets.web.redis.domain.user.UserStored;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Api(value = "资产接口v1", description = "资产相关接口")
@RestController
@RequestMapping("/api/v1/wealth")
public class WealthController extends BaseController {
    @Resource
    private WealthFlowDao wealthFlowDao;
    @Resource
    private UserDao userDao;

    @ApiOperation(value = "用户提现", response = Long.class)
    @PostMapping("/withdraw")
    public ResponseResult withdraw(@RequestBody WithdrawParams params, HttpServletRequest request) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);
        final Set<ConstraintViolation<WithdrawParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }

        final UserStored user = globalService.getUser(params.resolveUserId());
        if (null == user) return errorMessage(MessageCode.code_777);

        if (!globalVariablesService.canProceedCustom("withdraw" + params.resolveUserId(), 10)) {
            return errorMessage(MessageCode.code_444, "10秒之内，只能提交一次，提现慢慢来，防止重复操作！");
        }

        return (ResponseResult) redisSyncLocker.idLock(params.resolveUserId(), o -> {
            final long surplus = globalService.getUserWealthOfMoney(params.resolveUserId(), WealthType.money);
            if (surplus < params.getMoney()) return errorMessage(MessageCode.code_444, "余额不够您的提现金额！");

            FlowMessage message = new FlowMessage(params.resolveUserId(), FlowType.wealthChange, HttpTools.getIp(request), params.resolveUserId());
            message.addData("offset", -params.getMoney());
            message.addData("operateType", OperateType.withdraw);
            message.addData("detail", "用户主动提现");
            message.addData("payType", PayType.surplus.name());
            message.addData("unionType", UnionType.manager.name());
            message.addData("flowStatus", FlowStatus.verify.name());
            messageProducer.flow(message);

            globalService.removeRedisWealthOfMoney(params.resolveUserId(), WealthType.money);
            return successMessage(surplus - params.getMoney());
        });
    }

    @ApiOperation("用户提现审核")
    @PostMapping("/withdrawCheck")
    public ResponseResult withdrawCheck(@RequestBody WithdrawCheckParams params, HttpServletRequest request) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);
        final Set<ConstraintViolation<WithdrawCheckParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }
        final WealthFlow flow = wealthFlowDao.findById(params.getWithdrawId()).orElse(null);
        if (null == flow) return errorMessage(MessageCode.code_444, "您操作了不存在的流水记录！");
        return (ResponseResult) redisSyncLocker.idLock(flow.getUserId(), o -> {
            if (!params.isSuccess()) {//返账
                flow.setFlowStatus(FlowStatus.failed);
                flow.setDetail(params.getFailedReason());
                wealthFlowDao.save(flow);

                FlowMessage message = new FlowMessage(flow.getUserId(), FlowType.wealthChange, HttpTools.getIp(request), flow.getId());
                message.addData("offset", Math.abs(flow.getOffset()));
                message.addData("operateType", OperateType.restoreWithdraw.name());
                message.addData("detail", "审核失败返还");
                message.addData("payType", PayType.surplus.name());
                message.addData("unionType", UnionType.flow.name());
                message.addData("flowStatus", FlowStatus.success.name());
                messageProducer.flow(message);
            } else {
                try {
                    final User user = userDao.findById(flow.getUserId()).orElse(null);
                    if (null == user) return errorMessage(MessageCode.code_444, "该流水的主人不存在了？！");
                    final long amount = Math.abs(flow.getOffset());
                    final String description = "提现¥" + NumberUtil.fen2Yuan(Math.abs(flow.getOffset())) + "元";
                    final EntPayRequest entPayRequest = EntPayRequest.newBuilder().amount(Math.toIntExact(amount))
                            .description(description).openid(user.getOpenId())
                            .partnerTradeNo(Global.OrderNumber.getTransfer(flow.getId(), flow.getCreateTime()))
                            .checkName("NO_CHECK")
                            .spbillCreateIp("47.110.137.151")
                            .build();

                    final EntPayResult entPayResult = wxPayService.getEntPayService().entPay(entPayRequest);
                    if ("SUCCESS".equals(entPayResult.getReturnCode()) && "SUCCESS".equals(entPayResult.getResultCode())) {
                        flow.setFlowStatus(FlowStatus.success);
                        wealthFlowDao.save(flow);
                        //globalService.addNewWithdrawSuccess(Math.abs(flow.getOffset()));
                    }
                } catch (WxPayException e) {
                    e.printStackTrace();
                    return errorMessage(MessageCode.code_302, e.getErrCodeDes());
                }
            }
            return successMessage();
        });
    }

    @ApiOperation("用户资产余额")
    @PostMapping("/wealth")
    public ResponseResult wealth(@RequestBody TokenParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);
        final Set<ConstraintViolation<TokenParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }
        JSONObject object = new JSONObject();
        for (WealthType wealthType : WealthType.values()) {
            object.put(wealthType.name(), globalService.getUserWealthOfMoney(params.resolveUserId(), wealthType));
        }
        return successMessage(object);
    }

    @ApiOperation(value = "用户资产流水", response = WealthFlow.class)
    @PostMapping("/wealthFlows")
    public ResponseResult wealthFlows(@RequestBody UserWealthPagingParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);
        final Set<ConstraintViolation<UserWealthPagingParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }

        Specification<WealthFlow> specification = (Specification<WealthFlow>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("userId"), params.resolveUserId()));
            if (null != params.getWealthType()) {
                predicates.add(criteriaBuilder.equal(root.get("wealthType"), params.getWealthType()));
            }
            if (null != params.getOperateType()) {
                predicates.add(criteriaBuilder.equal(root.get("operateType"), params.getOperateType()));
            }
            if (null != params.getFlowStatus()) {
                predicates.add(criteriaBuilder.equal(root.get("flowStatus"), params.getFlowStatus()));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<WealthFlow> page = wealthFlowDao.findAll(specification, PageRequest.of(params.getPage(), params.getLimit(), new Sort(Sort.Direction.DESC, "id")));
        List<WealthFlow> list = new ArrayList<>();
        page.map(flow -> {
            list.add(flow);
            return flow;
        });
        return successMessage(list, new KeyValue("page", params.getPage()), new KeyValue("total", page.getTotalElements()));
    }

    @ApiOperation("用户资产接口变动")
    @PostMapping("/wealthChange")
    public ResponseResult wealthChange(@RequestBody UserWealthChangeParams params, HttpServletRequest request) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);
        final Set<ConstraintViolation<UserWealthChangeParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }

        final long surplus = globalService.getUserWealthOfMoney(params.getTargetUserId(), params.getWealthType());
        if (params.getOffset() > 1000000) return errorMessage(MessageCode.code_444, "单次变动金额不能超过一万");
        if (surplus + params.getOffset() < 0) return errorMessage(MessageCode.code_444, "变动后，用户资产为负，请重新填写变动值");

        if (!globalVariablesService.canProceedCustom("UserWealthChange" + params.getTargetUserId(), 300)) {
            return errorMessage(MessageCode.code_444, "在五分钟内，每个用户只能做一次资产变动，防止重复操作！");
        }

        final UserStored user = globalService.getUser(params.getTargetUserId());
        if (null == user) return errorMessage(MessageCode.code_404, "用户不存在");

        final FlowMessage message = new FlowMessage(params.getTargetUserId(), FlowType.wealthChange, HttpTools.getIp(request), params.resolveUserId());
        message.addData("wealthType", params.getWealthType());
        message.addData("offset", params.getOffset());
        message.addData("operateType", OperateType.human);
        message.addData("detail", params.getReason());
        message.addData("payType", PayType.surplus.name());
        message.addData("unionType", UnionType.manager.name());
        message.addData("flowStatus", FlowStatus.success.name());
        messageProducer.flow(message);

        return successMessage();
    }

}
